﻿using System;
using System.Diagnostics;
using System.Net;
using System.Windows;
using Microsoft.Phone.BackgroundAudio;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Xml.Linq;
using Microsoft.Phone.Shell;
using GlobalClass;

namespace AudioPlaybackAgent
{
    public class AudioPlayer : AudioPlayerAgent
    {
        private static volatile bool _classInitialized;
        // A playlist made up of AudioTrack items.
        public static List<AudioTrack> _playList;
        private IsolatedStorageFile fileStorage;
        private IsolatedStorageSettings aSettings;

        /// <remarks>
        /// Экземпляры AudioPlayer могут совместно использовать один и тот же процесс. 
        /// Статические поля могут использоваться для распределения состояния между экземплярами AudioPlayer
        /// или для взаимодействия с агентом потокового аудио.
        /// </remarks>
        public AudioPlayer()
        {
            fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
            aSettings = IsolatedStorageSettings.ApplicationSettings;
            if (!_classInitialized)
            {
                _classInitialized = true;
                // Подпишитесь на обработчик управляемых исключений
                Deployment.Current.Dispatcher.BeginInvoke(delegate
                {
                    Application.Current.UnhandledException += AudioPlayer_UnhandledException;
                });
            }
            _playList = new List<AudioTrack>();
        }

        /// Код для выполнения на необработанных исключениях
        private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // Произошло необработанное исключение; перейти в отладчик
                System.Diagnostics.Debugger.Break();
            }
        }

        /// <summary>
        /// Вызывается при изменении состояния воспроизведения, за исключением состояния ошибки (см. OnError)
        /// </summary>
        /// <param name="player">BackgroundAudioPlayer</param>
        /// <param name="track">Дорожка, воспроизводимая во время изменения состояния воспроизведения</param>
        /// <param name="playState">Новое состояние воспроизведения проигрывателя</param>
        /// <remarks>
        /// Изменения состояния воспроизведения невозможно отменить. Они вызываются, даже если изменение состояния
        /// было вызвано самим приложением при условии, что в приложении используется обратный вызов.
        ///
        /// Важные события playstate: 
        /// (а) TrackEnded: вызывается, когда в проигрывателе нет текущей дорожки. Агент может задать следующую дорожку.
        /// (б) TrackReady: звуковая дорожка задана и готова для воспроизведения.
        ///
        /// Вызовите NotifyComplete() только один раз после завершения запроса агента, включая асинхронные обратные вызовы.
        /// </remarks>
        protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
        {
            /*switch (playState)
            {
                case PlayState.TrackEnded:
                    player.Track = GetPreviousTrack();
                    break;
                case PlayState.TrackReady:
                    player.Play();
                    break;
                case PlayState.Shutdown:
                    // TODO: обработайте здесь состояние отключения (например, сохраните состояние)
                    break;
                case PlayState.Unknown:
                    break;
                case PlayState.Stopped:
                    break;
                case PlayState.Paused:
                    break;
                case PlayState.Playing:
                    break;
                case PlayState.BufferingStarted:
                    break;
                case PlayState.BufferingStopped:
                    break;
                case PlayState.Rewinding:
                    break;
                case PlayState.FastForwarding:
                    break;
            }*/

            switch (playState)
            {
                case PlayState.TrackReady:
                    player.Play();
                    break;

                case PlayState.TrackEnded:
                    PlayNextTrack(player);
                    break;
            }

            NotifyComplete();
        }


        /// <summary>
        /// Вызывается при запросе пользователем действия с помощью пользовательского интерфейса приложения или системы
        /// </summary>
        /// <param name="player">BackgroundAudioPlayer</param>
        /// <param name="track">Дорожка, воспроизводимая во время действия пользователя</param>
        /// <param name="action">Действие, запрошенное пользователем</param>
        /// <param name="param">Данные, связанные с запрошенным действием.
        /// В текущей версии этот параметр используется только с действием поиска
        /// для обозначения запрошенного положения в звуковой дорожке</param>
        /// <remarks>
        /// Действия пользователя не изменяют автоматически состояние системы; за выполнение действий
        /// пользователя, если они поддерживаются, отвечает агент.
        ///
        /// Вызовите NotifyComplete() только один раз после завершения запроса агента, включая асинхронные обратные вызовы.
        /// </remarks>
        protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
        {
            /*switch (action)
            {
                case UserAction.Play:
                    if (player.PlayerState != PlayState.Playing)
                    {
                        player.Play();
                    }
                    break;
                case UserAction.Stop:
                    player.Stop();
                    break;
                case UserAction.Pause:
                    player.Pause();
                    break;
                case UserAction.FastForward:
                    player.FastForward();
                    break;
                case UserAction.Rewind:
                    player.Rewind();
                    break;
                case UserAction.Seek:
                    player.Position = (TimeSpan)param;
                    break;
                case UserAction.SkipNext:
                    player.Track = GetNextTrack();
                    break;
                case UserAction.SkipPrevious:
                    AudioTrack previousTrack = GetPreviousTrack();
                    if (previousTrack != null)
                    {
                        player.Track = previousTrack;
                    }
                    break;
            }*/
            switch (action)
            {
                case UserAction.Play:
                    PlayTrack(player);
                    aSettings["isPauseLastUserAction"] = false;
                    break;

                case UserAction.Pause:
                    player.Pause();
                    aSettings["isPauseLastUserAction"] = true;
                    break;

                case UserAction.SkipPrevious:
                    PlayPreviousTrack(player);                 
                    break;

                case UserAction.SkipNext:
                    PlayNextTrack(player);
                    break;

                case UserAction.Seek:
                    player.Position = (TimeSpan)param;
                    aSettings["isPauseLastUserAction"] = false;
                    break;
            }

            NotifyComplete();
        }


        /// <summary>
        /// Реализует логику для получения следующего экземпляра AudioTrack.
        /// В списке воспроизведения источником может быть файл, веб-запрос и т. д.
        /// </summary>
        /// <remarks>
        /// Универсальный код ресурса (URI) AudioTrack определяет источник, которым может быть:
        /// (а) файл в изолированной памяти (относительный URI, представляет путь в изолированной памяти)
        /// (б) URL-адрес HTTP (абсолютный URI)
        /// (в) MediaStreamSource (null)
        /// </remarks>
        /// <returns>экземпляр AudioTrack или значение null, если воспроизведение завершено</returns>
        private AudioTrack GetNextTrack()
        {
            // TODO: добавьте логику для получения следующей звуковой дорожки

            AudioTrack track = null;

            // укажите дорожку

            return track;
        }


        /// <summary>
        /// Реализует логику для получения предыдущего экземпляра AudioTrack.
        /// </summary>
        /// <remarks>
        /// Универсальный код ресурса (URI) AudioTrack определяет источник, которым может быть:
        /// (а) файл в изолированной памяти (относительный URI, представляет путь в изолированной памяти)
        /// (б) URL-адрес HTTP (абсолютный URI)
        /// (в) MediaStreamSource (null)
        /// </remarks>
        /// <returns>экземпляр AudioTrack или значение null, если предыдущая дорожка не разрешена</returns>
        private AudioTrack GetPreviousTrack()
        {
            // TODO: добавьте логику для получения предыдущей звуковой дорожки

            AudioTrack track = null;

            // укажите дорожку

            return track;
        }

        /// <summary>
        /// Вызывается в случае ошибки воспроизведения, например, если звуковая дорожка не загружается правильно
        /// </summary>
        /// <param name="player">BackgroundAudioPlayer</param>
        /// <param name="track">Дорожка, в которой произошла ошибка</param>
        /// <param name="error">Произошедшая ошибка</param>
        /// <param name="isFatal">При значении true воспроизведение дорожки невозможно и будет остановлено</param>
        /// <remarks>
        /// Вызов этого метода во всех случаях не гарантируется. Например, если в фоновом агенте 
        /// произошло необработанное исключение, он не будет вызываться для обработки своих ошибок.
        /// </remarks>
        protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal)
        {
            if (isFatal)
            {
                Abort();
            }
            else
            {
                NotifyComplete();
            }

        }

        /// <summary>
        /// Вызывается при отмене запроса агента
        /// </summary>
        /// <remarks>
        /// После отмены запроса агент завершает работу в течение 5 секунд
        /// путем вызова NotifyComplete()/Abort().
        /// </remarks>
        protected override void OnCancel()
        {

        }

        private void PlayNextTrack(BackgroundAudioPlayer player)
        {
            AddSongsToPlaylist();
            if (GetCurrentTrackNumber("next") >= _playList.Count)
            {
                GetCurrentTrackNumber(0);
            }
            aSettings["isPauseLastUserAction"] = false;
            PlayTrack(player);
        }

        private void PlayPreviousTrack(BackgroundAudioPlayer player)
        {
            AddSongsToPlaylist();
            if (GetCurrentTrackNumber("prev") < 0)
            {
                GetCurrentTrackNumber(_playList.Count - 1);
            }
            aSettings["isPauseLastUserAction"] = false;
            PlayTrack(player);
        }

        private void PlayTrack(BackgroundAudioPlayer player)
        {
            if (aSettings.Contains("isPauseLastUserAction") && (bool) aSettings["isPauseLastUserAction"])
            {
                player.Play();
            }
            else
            {
                AddSongsToPlaylist();
                Dictionary<string, string> options = Global.GetPlayingOptions();
                if (options["isRandomPlaying"] == "True")
                {
                    player.Track = _playList[GetCurrentTrackNumber(GetRandomCurrentTrack())];
                }
                else
                {
                    player.Track = _playList[GetCurrentTrackNumber()];
                }
                
            }
        }

        private void AddSongsToPlaylist()
        {
            if (fileStorage.FileExists("shared/transfers/playList.xml"))
            {
                using (IsolatedStorageFileStream isostream = fileStorage.OpenFile("shared/transfers/playList.xml", FileMode.Open))
                {
                    XDocument songsList = XDocument.Load(isostream);
                    var audioElements = songsList.Root.Elements();
                    foreach (var audioElement in audioElements)
                    {
                        _playList.Add(
                            new AudioTrack(
                                new Uri(audioElement.Element("url").Value),
                                audioElement.Element("artist").Value,
                                audioElement.Element("title").Value,
                                null,
                                null));
                    }
                }
            }
        }

        private int GetCurrentTrackNumber(object course = null)
        {
            int currNmb = 0;
            Dictionary<string, string> options = Global.GetPlayingOptions();
            if (fileStorage.FileExists("currentTrackNumber.txt"))
            {
                StreamReader fileReader = null;
                try
                {
                    fileReader = new StreamReader(new IsolatedStorageFileStream("currentTrackNumber.txt", FileMode.Open, fileStorage));
                    currNmb = Int32.Parse(fileReader.ReadLine());
                    fileReader.Close();
                }
                catch
                {
                }
                if (course != null)
                {
                    if (course.GetType().ToString() == "System.Int32")
                    {
                        currNmb = Convert.ToInt32(course);
                    }
                    if (options["isRepeatPlaying"] == "False")
                    {
                        if (course.ToString() == "prev")
                        {
                            currNmb--;
                        }
                        else if (course.ToString() == "next")
                        {
                            currNmb++;
                        }
                    }
                    try
                    {
                        StreamWriter fileWriter = new StreamWriter(new IsolatedStorageFileStream(
                                "currentTrackNumber.txt", FileMode.Create, fileStorage));
                        fileWriter.WriteLine(currNmb);
                        fileWriter.Close();
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Message);
                    }
                }
            }
            return currNmb;
        }

        private int GetRandomCurrentTrack()
        {
            Random random = new Random();
            return random.Next(_playList.Count - 1);
        }
    }
}
